`timescale 1ns / 1ps

module bypass_unit
#(
    parameter BIT   = 8,
    parameter N_IO  = 2,
    parameter N_CH  = 768,
    parameter COL   = 40,
    parameter RAM_TYPE = "block",       // auto, block, distributed, register
    parameter RAM_LATENCY = 2
)
(
    input   clk,
    input   rst,

    input   i_vld,
    output  i_rdy,
    input   [N_IO*BIT-1 : 0]    i_data,

    output  o_vld,
    input   o_rdy,
    output  [N_IO*BIT-1 : 0]    o_data
);

localparam WIDTH = N_IO * BIT;
localparam N_BLK = N_CH / 2 / N_IO;
localparam DEPTH_FIFO_A = 4 * COL * N_BLK;
localparam DEPTH_FIFO_B = 4 * COL * N_BLK;

wire                ai_rdy;
wire                ai_vld;
wire                ao_rdy;
wire                ao_vld;
wire [WIDTH-1 : 0]  ao_data;

wire                bi_rdy;
wire                bi_vld;
wire                bo_rdy;
wire                bo_vld;
wire [WIDTH-1 : 0]  bo_data;

reg  i_sel;
wire i_ena;
wire i_last;

reg  o_sel;
wire o_ena;
wire o_last;

assign  i_rdy =  i_sel ? bi_rdy : ai_rdy;
assign ai_vld = ~i_sel & i_vld;
assign bi_vld =  i_sel & i_vld;

assign  o_vld =  o_sel ? bo_vld : ao_vld;
assign o_data =  o_sel ? bo_data : ao_data;
assign ao_rdy = ~o_sel & o_rdy;
assign bo_rdy =  o_sel & o_rdy;

assign i_ena = i_rdy & i_vld;
assign o_ena = o_rdy & o_vld;

always @(posedge clk)
begin
    if (rst) begin
        i_sel <= 1'b0;
        o_sel <= 1'b0;
    end
    else begin
        if (i_ena)
            i_sel <= i_sel ^ i_last;
        if (o_ena)
            o_sel <= o_sel ^ o_last;
    end
end

zq_counter #(
    .N      ( COL * N_BLK )
) inst_cnt_in
(
    .clk    (clk),
    .rst    (rst),
    .clken  (i_ena),
    .last   (i_last),
    .out    ()
);
zq_counter #(
    .N      ( N_BLK )
) inst_cnt_out
(
    .clk    (clk),
    .rst    (rst),
    .clken  (o_ena),
    .last   (o_last),
    .out    ()
);

zq_fifo #(
    .WIDTH      ( WIDTH         ),
    .DEPTH      ( DEPTH_FIFO_A  ),
    .RAMTYPE    ( RAM_TYPE      ),
    .RAMLATENCY ( RAM_LATENCY   )
)
inst_fifoA (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .in_vld                  ( ai_vld   ),
    .in_rdy                  ( ai_rdy   ),
    .din                     (  i_data  ),

    .out_vld                 ( ao_vld   ),
    .out_rdy                 ( ao_rdy   ),
    .dout                    ( ao_data  )
);
zq_fifo #(
    .WIDTH      ( WIDTH         ),
    .DEPTH      ( DEPTH_FIFO_B  ),
    .RAMTYPE    ( RAM_TYPE      ),
    .RAMLATENCY ( RAM_LATENCY   )
)
inst_fifoB (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .in_vld                  ( bi_vld   ),
    .in_rdy                  ( bi_rdy   ),
    .din                     (  i_data  ),

    .out_vld                 ( bo_vld   ),
    .out_rdy                 ( bo_rdy   ),
    .dout                    ( bo_data  )
);

endmodule
